/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <assert.h>
#include <string.h>

// this should be enough
static char buf[65536] = {};
static char* buf_ptr;
static char code_buf[65536 + 256] = {}; // a little larger than `buf`
// nemu使用uint64_t, 此处也使用 uint64_t, 保证运算的一致性
static char *code_format = "#include <stdio.h>\n"
                           "#include <stdint.h>\n"
                           "int main() { "
                           "    uint64_t result = (uint64_t)%s; "
                           "    printf(\"%%lu\", result); "
                           "    return 0; "
                           "}";

#define SPRINTF(fmt, ...) buf_ptr += sprintf(buf_ptr, fmt, ##__VA_ARGS__)
#define ARRAY_LENGTH(arr) (sizeof(arr) / sizeof(arr[0]))
#define N 256

inline static int choose(int n) {
    return rand() % n;
}

/*
* expr        := factor ( op factor ) *
* factor      := num | "(" expr ")"
* op   := "+" | "-" | "*" | "/"
*/
static void gen_expr();
static void gen_factor();
static void gen_op();
static void gen_num();
static void gen_space();

// expr        := factor ( op factor ) *
static void gen_expr() {
    gen_factor();
    while (true) {
        // 防止buf溢出
        if (buf_ptr - buf > N) return;
        // 1 / 10 的概率生成 0
        if (!choose(10)) return;
        gen_op();
        gen_space();
        gen_factor();
        gen_space();
    }
}

// factor      := num | "(" expr ")"
static void gen_factor() {
    if (choose(2) || buf_ptr - buf > N) {
        gen_num();
        return;
    }
    SPRINTF("%s", "(");
    gen_expr();
    SPRINTF("%s", ")");
}

static void gen_op() {
    // 大概率生成 "+" 和 "-", 小概率生成 "*" 和 "/".
    static char* ops[] = { "+", "+", "+", "+", "-", "-", "-", "-", "*", "*", "/", };
    SPRINTF("%s", ops[choose(ARRAY_LENGTH(ops))]);
}

static void gen_num() {
    // 尽可能避免大数运算产生 overflow
    SPRINTF("%ulu", rand() % N);
}

static void gen_space() {
    int n = rand() % 2;
    for (int i = 0; i < n; ++i) {
        SPRINTF(" ");
    }
}

static void gen_rand_expr() {
    gen_expr();
}

int main(int argc, char *argv[]) {
    int seed = time(0);
    srand(seed);
    int loop = 1;
    if (argc > 1) {
        sscanf(argv[1], "%d", &loop);
    }
    int i = 0;
    while (i < loop) {
        buf_ptr = buf;
        gen_rand_expr();

        sprintf(code_buf, code_format, buf);

        FILE *fp = fopen("/tmp/.code.c", "w");
        assert(fp != NULL);
        fputs(code_buf, fp);
        fclose(fp);

        // 如果生成的表达式出现 除0 或 overflow, 则编译失败, 重新生成
        int ret = system("gcc -Wall -Werror /tmp/.code.c -o /tmp/.expr");
        if (ret != 0)
            continue;

        fp = popen("/tmp/.expr", "r");
        assert(fp != NULL);

        uint64_t result;
        ret = fscanf(fp, "%lu", &result);
        pclose(fp);

        // 删除后缀 lu
        for (char* p = buf; p < buf_ptr; ++p) {
            if (*p == 'l' || *p == 'u') *p = ' ';
        }

        printf("%lu %s\n", result, buf);
        i ++;
    }
    return 0;
}